//
//  ParticlesView.m
//  TouchEffects
//
//  Created by KuoHsinTu on 2011/9/27.
//  Copyright 2011年 __MyCompanyName__. All rights reserved.
//

#import "ParticlesView.h"


#define MAX_PARTICLES 512


/* constants */
#define MIN_PARTICLE_SPACING 40
#define GRAVITY_METERS_PER_SCREEN_WIDTH 7.272727
#define REPULSION_PIXELS_PER_METER 10
#define PARTICLE_MASS 1
#define PARTICLE_RADIUS 5
#define PARTICLE_DIAMETER 2 * PARTICLE_RADIUS
#define GRAVITY_PARTICLE_DAMPING_FACTOR 0.99
#define REPULSION_PARTICLE_DAMPING_FACTOR 0.92
#define GRAVITY_WELL_MASS 100
#define MAGNETIC_MAGNITUDE 50000
#define GRAVITY_TETHER_SPRING_CONSTANT .1
#define REPULSION_TETHER_SPRING_CONSTANT .5
#define STEP_HZ 60.0
#define TOUCH_POINT_RADIUS 60
#define RAINBOW_STRIP_DOT_WIDTH 20
#define RAINBOW_STRIP_DOT_MAX 210
#define BEE_SIZE 20

/* mode switches */
#define PHYSICS_MODE_GRAVITY_WELL 0
#define PHYSICS_MODE_REPULSION 1
#define TETHER_MODE_OFF 0
#define TETHER_MODE_ON 1
#define DRAW_MODE_CIRCLES 0
#define DRAW_MODE_RAINBOWS 1
#define DRAW_MODE_BALLS 2
#define DRAW_MODE_BEES 3
#define BLUR_MODE_OFF 0
#define BLUR_MODE_ON 1


/*
var CANVAS_WIDTH;
var CANVAS_HEIGHT;
var CANVAS_LEFT;
var CANVAS_TOP;
var GRAVITY_PIXELS_PER_METER;
*/

typedef struct {
    
    float x; 
    float y; 
    float vx;
    float vy;
    float startX;
    float startY;
    float hue;

} Particle;

Particle particleArray[MAX_PARTICLES];


@implementation ParticlesView

- (void)dealloc
{
    [super dealloc];
}

- (id)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) 
    {
        [self initVariables];
        [self initParticles];
        
        
        animationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/20.0
                                                          target:self 
                                                        selector:@selector(updateFrame) 
                                                        userInfo:nil 
                                                         repeats:YES];
         
        
        //[self updateFrame];
    }
    return self;
}

- (void)initVariables
{
    canvasElm.size.width = 800;
    canvasElm.size.height = 600;
    canvasElm.origin.x = 0;
    canvasElm.origin.y = 0;
    
    physicsMode = PHYSICS_MODE_GRAVITY_WELL;
    tetherMode = TETHER_MODE_OFF;
    drawMode = DRAW_MODE_RAINBOWS;
    blurMode = BLUR_MODE_ON;
    
    nParticles = 0;
}

- (void)initParticles
{
    CANVAS_WIDTH = canvasElm.size.width;
    CANVAS_HEIGHT = canvasElm.size.height;
    CANVAS_LEFT = canvasElm.origin.x;
    CANVAS_TOP = canvasElm.origin.y;
    
    GRAVITY_PIXELS_PER_METER = CANVAS_WIDTH / GRAVITY_METERS_PER_SCREEN_WIDTH;
    
    int elemColumns = CANVAS_WIDTH / MIN_PARTICLE_SPACING;
    int elemRows = CANVAS_HEIGHT / MIN_PARTICLE_SPACING;
    int actualColumnSpacing = CANVAS_WIDTH / elemColumns;
    int actualRowSpacing = CANVAS_HEIGHT / elemRows;
    
    
    int i, j, idx = 0;
    
    for (i = 0; i < elemColumns; i++) {
        for (j = 0; j < elemRows; j++) {
            float elmX = i * actualColumnSpacing + actualColumnSpacing / 2;
            float elmY = j * actualRowSpacing + actualRowSpacing / 2;
            
            Particle aParticle = { elmX,elmY,  0,0,  elmX,elmY,  0 };
            particleArray[idx++] = aParticle;

            //NSLog(@"Particle at:%d, elmX = %f, elmY = %f", idx-1, elmX, elmY);
        }
    }
    
    nParticles = idx;
}

- (void)drawRect:(CGRect)rect
{
    /*
    // prep for draw
    switch(blurMode) {
        case BLUR_MODE_OFF:
        {
            context.globalAlpha = 1;
            break;
        }
        case BLUR_MODE_ON:
        {
            context.globalAlpha = 0.2;
            break;
        }
    }
     */
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetAlpha(context, 0.2);
    
    //CGContextBeginTransparencyLayer(context, NULL);
    
    CGContextSetRGBFillColor(context, 0, 0, 0, 1);
    CGContextFillRect(context, CGRectMake(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT));

    //CGContextEndTransparencyLayer(context); 
    
    CGContextSetAlpha(context, 1);
    
    // draw glows for active contacts
    //drawTouchGlows();
    
    /*
    // draw appropriate particles
    switch(drawMode) {
        case DRAW_MODE_RAINBOWS : drawRainbows(); break;
        case DRAW_MODE_BALLS : drawBalls(); break;
        case DRAW_MODE_CIRCLES : drawCircles(); break;
        case DRAW_MODE_BEES : drawBees(); break;
    }
     */
     
    [self drawBalls];
    
    // Other browsers that do not yet support feature
    
}

- (void)drawBalls
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    for (int i = 0; i < nParticles; i++) 
    {
        CGContextSaveGState(context);
        
        CGContextTranslateCTM(context, particleArray[i].x, particleArray[i].y);
        
        UIImage *image = [UIImage imageNamed:@"ball.png"];
        CGContextDrawImage(context, CGRectMake(-PARTICLE_RADIUS, -PARTICLE_RADIUS, PARTICLE_DIAMETER, PARTICLE_DIAMETER), image.CGImage);

        
        CGContextRestoreGState(context);
        
    }
}

- (void)updateFrame
{
    [self stepPhysics];
    [self setNeedsDisplay];
}

- (void)stepPhysics
{
    // apply force to update the new frame's velocity
    [self applyGravity];
    
    /*
    switch(physicsMode) 
    {
        case PHYSICS_MODE_GRAVITY_WELL : applyGravity(); break;
        case PHYSICS_MODE_REPULSION : applyRepulsion(); break;
    }
     */
    
    /*
    if (tetherMode == TETHER_MODE_ON) 
    {
        applyTether();
    }
     */
    
    // dampen velocity
    [self applyDamping];
    // apply velocity to update the new frame's position
    [self updatePosition];
    
    /*
    // recalc hue based on new velocity if rainbows
    if(drawMode == DRAW_MODE_RAINBOWS) 
    {
        calculateRainbowColor();
    }
     */
}

- (void)applyDamping
{
    float dampingFactor;
    
    switch(physicsMode) {
        case PHYSICS_MODE_GRAVITY_WELL : dampingFactor = GRAVITY_PARTICLE_DAMPING_FACTOR; break;
        case PHYSICS_MODE_REPULSION : dampingFactor = REPULSION_PARTICLE_DAMPING_FACTOR; break;
    }
    
    for (int i = 0; i < nParticles; i++)
    {
        Particle *ptrP = &particleArray[i];
        ptrP->vx *= dampingFactor;
        ptrP->vy *= dampingFactor;
    }
}

- (void)applyGravity
{
    CGPoint distanceVec; 
    float distanceVecMag, forceVecMag;
    
    //touchPoints.forEach(function(gw) 
    //{
    CGPoint gw = CGPointMake(0, 600);
    
        int size = sizeof(particleArray)/sizeof(Particle);
        for (int i = 0; i < size; i++) 
        {
            Particle p = particleArray[i];
            distanceVec = CGPointMake((gw.x - p.x), (gw.y - p.y));
            distanceVecMag = sqrt(pow(distanceVec.x, 2) + pow(distanceVec.y, 2));
            forceVecMag = GRAVITY_WELL_MASS * PARTICLE_MASS / MAX(pow(distanceVecMag / GRAVITY_PIXELS_PER_METER, 2), PARTICLE_RADIUS);
            
            Particle *ptrP = &particleArray[i];
            ptrP->vx += forceVecMag * distanceVec.x / distanceVecMag;
            ptrP->vy += forceVecMag * distanceVec.y / distanceVecMag;
            
            //NSLog(@"DistVecMag:%f, ForceVecMag:%f", distanceVecMag, forceVecMag);
        }
    //}
}

- (void)updatePosition
{
    for (int i = 0; i < nParticles; i++) 
    {
        Particle *p = &particleArray[i];
        p->x += p->vx / STEP_HZ;
        p->y += p->vy / STEP_HZ;
        
        //if (i == 100)
        //    NSLog(@"p.x:%f, p.y:%f", p->x, p->y);
    }
}


@end
